/* 
 * This program tests the callback associated to a socked descriptor. This
 * program is used in conjunction with fd_client, and is executed as follows: 
 *  > fd_server <cfg_file.xml>
 *  > fd_client <server_IP_addr> 
 * (server_IP_addr is printed by fd_server)
 *
 * This program implements the functionality of a simple server that 
 * displays the content of the messages sent by a client. 
 *
 * ACK: Most of the code is borrowed from:
 *  http://pont.net/socket/prog/udpServer.c
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>    /* timeval{} for select() */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h> /* close() */
#include <string.h> /* memset() */
#include <sys/utsname.h>
#include <errno.h>    

#include "../i3_client/i3_client_api.h"

#define LOCAL_SERVER_PORT 1500
#define MAX_MSG 100

void receive_cbk(int fd, char *data)
{
  int n, cliLen;
  struct sockaddr_in cliAddr;
  char msg[MAX_MSG];

  memset(msg, 0x0, MAX_MSG);
  
  /* receive message */
  cliLen = sizeof(cliAddr);
  n = recvfrom(fd, msg, MAX_MSG, 0, 
	       (struct sockaddr *) &cliAddr, &cliLen);
  
  if(n < 0) {
    printf("cannot receive data \n");
  }

  /* print received message */
  printf("from (ip=%s, port=%u) : %s \n", 
	 inet_ntoa(cliAddr.sin_addr),
	 ntohs(cliAddr.sin_port), msg);
}


int socket_create_bind(char *prog_name)
{  
  int fd, rc;
  struct sockaddr_in servAddr;

  /* socket creation */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(fd < 0) {
    printf("%s: cannot open socket \n", prog_name);
    exit(1);
  }

  /* bind local server port */
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servAddr.sin_port = htons(LOCAL_SERVER_PORT);
  rc = bind(fd, (struct sockaddr *) &servAddr,sizeof(servAddr));
  if(rc < 0) {
    printf("%s: cannot bind port number %d \n", 
	   prog_name, LOCAL_SERVER_PORT);
    exit(1);
  }

  printf("%s: waiting for data on port UDP %u\n", 
	   prog_name, LOCAL_SERVER_PORT);
  return fd;
}

  

int i3_main_loop(char *prog_name, char *cfg_file)
{
  int fd;
  struct timeval tv;

  /* initialize context */
  cl_init(cfg_file);

  /* create and bind a socket waiting for client to send data */
  fd = socket_create_bind(prog_name);
  printf("fd = %d\n", fd);

  /* add callback for fd */
  cl_register_fd_callback(fd, CL_FD_TYPE_READ, receive_cbk, NULL);

  /* loop forever */
  cl_loop();
  
  /* destroy context */
  cl_exit();
}


int main(int argc, char **argv)
{
  struct hostent *hptr;
  struct utsname myname;
  char str[INET6_ADDRSTRLEN];
  char **pptr;
 
  if (argc != 2) {
    printf("%s <cfg_file.xml>\n", argv[0]);
    exit(-1);
  }
  
  if (uname(&myname) < 0) {
    err_sys("uname error.\n");
    exit(-1);
  }

  if ((hptr = gethostbyname(myname.nodename)) == NULL) {
    err_sys("gethostbyname error\n");
    exit(-1);
  }

  printf("name = %s\n", hptr->h_name);
  for (pptr = hptr->h_addr_list; *pptr != NULL; pptr++) {
    printf("address = %s\n", inet_ntop(hptr->h_addrtype, 
				       *pptr, str, sizeof(str)));
  }

  i3_main_loop(argv[0], argv[1]);
  return -1;
}

